Skip to content

fix: repaint boo ui after a session resets the terminal (RIS)#70

Merged
kylecarbs merged 1 commit into
mainfrom
fix/ui-scroll-after-terminal-reset
Jun 15, 2026
Merged

fix: repaint boo ui after a session resets the terminal (RIS)#70
kylecarbs merged 1 commit into
mainfrom
fix/ui-scroll-after-terminal-reset

Conversation

@kylecarbs

Copy link
Copy Markdown
Member

What

In boo ui, mouse-wheel scrolling could get stuck on a specific session
until you exit and re-enter boo ui. A session that returns to the
primary screen via a full terminal reset (RIS, ESC c) instead of the
usual CSI ?1049 l left the client believing the app was still on the
alternate screen, so the wheel kept emitting arrow keys instead of
paging local scrollback.

Why

The wheel-over-viewport handler picks its behaviour from the per-session
app_alt flag (src/ui.zig), which is updated only by the daemon's
.screen message. .screen is sent from repaintTo, which the daemon
calls only on attach, on a C-a l redraw, or when its alt-screen filter
strips a 47/1047/1049 toggle (src/daemon.zig, src/altscreen.zig).

RIS resets the terminal to the primary screen without any such toggle,
so the filter never reported a switch, no .screen was sent, and
app_alt stayed stuck true. Mouse state recovered fine because RIS
passes through to the view terminal; only the out-of-band app_alt was
orphaned. Re-attaching (exit/re-enter, or C-a l) was the only fix.

Fix

In the daemon output path, capture the active screen before feeding each
chunk and repaint when it changed even if the filter saw no toggle. That
re-sends .screen (and re-seeds the view) after a RIS, and also hardens
against any other non-toggle screen change.

Test

  • New PTY integration test ui: wheel scrolls again after a session resets the terminal: enters the alt screen, issues ESC c, then
    asserts a wheel-up pages local scrollback. It times out (arrows sent)
    on the pre-fix daemon and passes with the fix.
  • zig build test (117 passed), zig build test-integration (69
    passed), and zig fmt --check are green locally.
Diagnosis / decision log

Wheel routing in wheelViewport (src/ui.zig):

  1. view.term.flags.mouse_event != .none -> forward the mouse to the app
  2. view.app_alt -> send arrow keys per tick
  3. otherwise -> page the view's local scrollback (shows a scrollback hint)

app_alt (default false) is a client View field set only by a
daemon .screen message. .screen is emitted exclusively by
repaintTo, whose only callers are attach, .redraw (C-a l), and a
filter-detected alt toggle. The alt-screen filter recognises only modes
47/1047/1049.

A RIS (ESC c):

  • returns the daemon terminal to the primary screen and clears mouse
    mode, but is not a 47/1047/1049 toggle, so result.switched stays
    false and no repaint/.screen is sent;
  • passes through to the view terminal, so mouse_event clears correctly
    there;
  • leaves app_alt stuck true, so the wheel takes branch 2 forever.

Re-attaching (exit/re-enter) builds a fresh View (app_alt defaults
false) and the attach repaint sends .screen = primary; C-a l
forwards C-a l as input, which the daemon maps to .redraw -> repaint ->
.screen = primary. Both prove the daemon terminal really is on the
primary screen after the reset.

Real-world trigger: anything that emits RIS while a session has been on
the alt screen, e.g. reset / tput reset, or a TUI/agent that
hard-resets the terminal on startup or exit instead of using rmcup.

Considered and rejected as the cause (covered by passing tests during
diagnosis): normal alt-screen exit, alt+mouse exit in either ordering,
and primary-screen mouse enable/disable all resync correctly because
every repaint's reset_state_sequence clears mouse and the formatter
re-emits the daemon's current modes.

Generated by Coder Agents on behalf of @kylecarbs.

In `boo ui` the wheel over the viewport pages local scrollback only when
the focused session is on the primary screen and has not asked for mouse
reporting. The daemon strips alternate-screen toggles from passthrough
and tells the client which screen the application is on out of band, via
a `.screen` message sent only when its filter detects a 47/1047/1049
toggle (plus on attach and redraw).

A full reset (RIS, `ESC c`) returns the terminal to the primary screen
without such a toggle, so the filter never reported a switch and the
client's `app_alt` stayed stuck true. The wheel then sent arrow keys
forever instead of scrolling, and only re-attaching (exit and re-enter
`boo ui`, or a C-a l redraw) recovered it.

Repaint whenever the active screen changes, even when the alt-screen
filter saw no toggle, so a fresh repaint and `.screen` reach the client.

Generated by Coder Agents on behalf of @kylecarbs.
@kylecarbs kylecarbs merged commit 1233765 into main Jun 15, 2026
5 checks passed
@kylecarbs kylecarbs deleted the fix/ui-scroll-after-terminal-reset branch June 15, 2026 18:52
@kylecarbs kylecarbs mentioned this pull request Jun 15, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant